#include "scooper.hpp"

Scooper::Scooper() {
  volume=NULL;
  root=NULL;
}

Scooper::Scooper(Volume *volume) {
  this->volume=volume;
}

Scooper::~Scooper() {
  delete root;
}

void Scooper::setVolume(Volume *volume) {
  this->volume=volume;
}

void Scooper::scoop() {
  if (volume!=NULL) {
    volume->findHeadVoxel();
    QList<Cluster> clusters,newClusters;
    Slice seed(Vector3i(1,1,1),1);
    seed.setOffset(volume->getHeadVoxel());
    Cluster cluster(seed);
    cluster.setSize(sqrt(3.0));
    volume->setVoxel(volume->getHeadVoxel(),0);
    ClusterNode *node=new ClusterNode(&cluster);
    root=node;
    node->setPosition(volume->getHeadVoxel().cast<double>()+Vector3d(0.5,0.5,0.5));
    clusters.append(cluster);
    while (!clusters.empty()) {
      while (!clusters.empty()) {
        Cluster cluster=clusters.takeLast();

        Vector3i offset=(cluster.slice().getOffset()-Vector3i(1,1,1)).cwiseMax(Vector3i(0,0,0));
        Vector3i offsetEnd=(offset+cluster.slice().size()+Vector3i(2,2,2)).cwiseMin(volume->size());

        //find 26-connected neighbours of current cluster
        Slice region(offsetEnd-offset,0);
        region.setOffset(offset);
        region.slice(cluster.slice().getOffset()-offset,
            cluster.slice().getOffset()-offset+cluster.slice().size())=cluster.slice();
        Slice grown(region.size(),0);
        grown.setOffset(offset);
        Slice object=volume->slice(offset,offsetEnd);
        SliceIterator growIt(region,grown,object);
        growIt.execute(SliceUtils::grow);

        //find connected components in grown region
        Slice labelled(grown.size(),0);
        labelled.setOffset(offset);
        SliceUtils::LabelParam labelParam;
        labelParam.offsets.clear();
        SliceIterator labelIt(grown,labelled);
        labelIt.execute(SliceUtils::label,&labelParam);

        for (int l=1;l<labelParam.label;l++) {
          //find bounding box of component l
          SliceUtils::BoundingBoxParam bbParam;
          bbParam.min=labelled.getEnd();
          bbParam.max=labelled.getStart();
          bbParam.label=l;
          SliceIterator bbIt(labelled);
          bbIt.execute(SliceUtils::boundingBox,&bbParam);

          //create slice consisting of component l
          Slice slice(bbParam.max-bbParam.min,0);
          slice.setOffset(offset+bbParam.min);
          Slice bbSlice(labelled.slice(bbParam.min,bbParam.max));

          SliceIterator filterIt(bbSlice,slice);
          SliceUtils::FilterParam filterParam;
          filterParam.srcValue=l;
          filterParam.destValue=1;
          filterIt.execute(SliceUtils::filter,&filterParam);

          //create new cluster from component l slice
          Cluster newCluster(slice);
          newCluster.setSize(slice.size().cast<double>().norm());
          ClusterNode *node=new ClusterNode(&newCluster);
          node->calculatePosition(&cluster);
          cluster.getNode()->addChild(node);
          Slice volSlice=volume->slice();
          newCluster.scoop(volSlice);
          newClusters.append(newCluster);
        }
      }
      clusters.append(newClusters);
      newClusters.clear();
    }
  }
}

void Scooper::write(const QString &fileName) const {
  nodeCounter=0;
  QFile file(fileName);
  file.open(QIODevice::WriteOnly);
  QXmlStreamWriter stream(&file);
  stream.setAutoFormatting(true);
  stream.setAutoFormattingIndent(2);
  stream.writeStartDocument();
  stream.writeStartElement("tree");
  stream.writeAttribute("delta",QString::number(volume->getVoxelSize()));
  stream.writeAttribute("x",QString::number(volume->position().x()));
  stream.writeAttribute("y",QString::number(volume->position().y()));
  stream.writeAttribute("z",QString::number(volume->position().z()));

  writeNode(root,stream);

  stream.writeEndElement();
  stream.writeEndDocument();
}

void Scooper::writeNode(const ClusterNode *node,QXmlStreamWriter &stream) const {
  Vector3d position=node->getPosition();
  stream.writeStartElement("node");
  stream.writeAttribute("id",QString::number(nodeCounter));
  stream.writeAttribute("x",QString::number(position.x()));
  stream.writeAttribute("y",QString::number(position.y()));
  stream.writeAttribute("z",QString::number(position.z()));
  nodeCounter++;
  foreach (const ClusterNode *node,node->getChildren()) {
    writeNode(node,stream);
  }
  stream.writeEndElement();
}
